<template>
  <div class="go_bang_wrap">
    <div class="go_bang_box">
      <header class="flex-between">
        <div class="flex-start player">
          <el-avatar :src="globalStore.userInfo?.avatar"></el-avatar>
          <div class="player_info">
            <el-text>{{ globalStore.userInfo.name }}</el-text>
            <p class="rank">菜鸟一级</p>
          </div>
        </div>
        <div v-if="opponent.name" class="flex-start player">
          <el-avatar :src="opponent?.avatar"></el-avatar>
          <div class="player_info">
            <el-text>{{ opponent.name }}</el-text>
            <p class="rank">菜鸟一级</p>
          </div>
        </div>
      </header>
      <div class="tip">{{ bang_action.tip }}</div>
      <canvas :key="bang_action.key" id="go_bang"  @click="stepChese"></canvas>
      <footer class="flex-center">
        <!-- <el-button type="primary" @click="restart">重新开始</el-button> -->
        <el-button v-if="!isMatching && !isStart" type="primary" @click="startMatch">开始匹配</el-button>
      </footer>
    </div>
    <el-dialog :width="300" center v-model="isMatching" :show-close="false" :close-on-press-escape="false"
      :close-on-click-modal="false">
      <p style="text-align: center;">{{ bang_action.tip }}</p>
      <template #footer>
        <span>
          <el-button type="primary" @click="cancelMatch">取消</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ElMessage, ElMessageBox, MessageBoxData } from 'element-plus';
import IoSocket from '@/utils/Socket';
import { GlobalStore } from '@/store'
const globalStore = GlobalStore()
const socket = ref()
const opponent = ref<{ avatar?: string, name?: string }>({})
const isStart = ref(false)
const isMatching = ref(false)
let context: CanvasRenderingContext2D; //canvas 2d实例
let timer: NodeJS.Timeout;
const bang_action = reactive({
  key: 0,
  turnMe: false, //是否本轮
  winWays: [], // 记录所有的赢法[[0,0,0],[1,0,0],[2,0,0],[3,0,0],[4,0,1], [1,0,1],[2,0,1],[3,0,1],[4,0,1],[5,0,1]] //前两个是坐标，后一个是赢法id
  wwScore: [], //每种赢法下自己棋子的坐标情况[[2,2,1],[3,2,1]] //可查看每种赢法下棋子的个数
  opwScore: [], //每种赢法下对手棋子的坐标情况[[2,2,1],[3,2,1]]
  over: false, //是否结束
  allBlank: [], //所有的空白子[[1,2], [2,3]]
  tip: "", //棋盘提示
}) as any;
onMounted(() => {
  initChessBoard();
  initWinWays();
});
const initChessBoard = () => {
  const canvas = document.getElementById("go_bang") as HTMLCanvasElement;
  const width = window.innerWidth<500?300:500;
  canvas.width = width;
  canvas.height = width;
  context = canvas.getContext("2d")!;
  context.strokeStyle = "#b9b9b9";
  for (let i = 0; i <= 15; i++) {
    context.moveTo(20, 20 + 30 * i); //起点
    context.lineTo(470, 20 + 30 * i); //终点
    context.stroke();
    context.moveTo(20 + 30 * i, 20); //起点
    context.lineTo(20 + 30 * i, 470); //终点
    context.stroke();
  }
};
const initWinWays = () => {
  let id = 0;
  // 横线赢法
  for (let i = 0; i < 15; i++) {
    for (let j = 0; j < 11; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i, j + k, id]);
      }
    }
  }
  // 竖线赢法
  for (let i = 0; i < 11; i++) {
    for (let j = 0; j < 15; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i + k, j, id]);
      }
    }
  }
  // 正斜线赢法
  for (let i = 4; i < 15; i++) {
    for (let j = 0; j < 11; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i - k, j + k, id]);
      }
    }
  }
  // 反斜线赢法
  for (let i = 0; i < 11; i++) {
    for (let j = 0; j < 11; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i + k, j + k, id]);
      }
    }
  }
  for (let i = 0; i < 15; i++) {
    for (let j = 0; j < 15; j++) {
      bang_action.allBlank.push([i, j]);
    }
  }
  // console.log(bang_action.winWays);
};
const stepChese = (e: MouseEvent) => {
  if (bang_action.over || !bang_action.turnMe) return;
  // bang_action.tip = `<p>ai思考中<span class="wait-loader"><span></p>`;
  console.log("你好");

  const x = Math.floor(e.offsetX / 30);
  const y = Math.floor(e.offsetY / 30);

  socket.value.emit("stepChese", { x, y })
  step(x, y)
};
const step = (x: number, y: number) => {
  const findIndex = bang_action.allBlank.findIndex(
    (_item: any[]) => JSON.stringify(_item) == JSON.stringify([x, y])
  );
  if (findIndex >= 0) {
    bang_action.allBlank.splice(findIndex, 1);
  } else {
    return;
  }

  context.beginPath();
  // 圆心坐标、半径、从0度到2pi度画个完整的圆出来
  context.fillStyle = bang_action.turnMe ? "black" : "red";
  context.arc(20 + 30 * x, 20 + 30 * y, 13, 0, 2 * Math.PI);
  context.closePath();
  context.fill();

  isOver(x, y);
  if (!bang_action.over) {

    bang_action.turnMe = !bang_action.turnMe;
    bang_action.tip = bang_action.turnMe ? "该您落子" : "等待对方落子"
  }
}
const isOver = (x: number, y: number, needImitate = false) => {
  // console.log(x, y);
  // 如果需要模拟棋局，需要将模拟后的棋盘状况返回

  const curScore = bang_action.turnMe
    ? bang_action.wwScore
    : bang_action.opwScore;

  let ImitateLayout: any[] = [];
  let num: number[] = [];

  bang_action.winWays.forEach((_item: any[]) => {
    const [_x, _y, _z] = _item;
    // 这颗棋存在赢法中
    if (x == _x && y == _y) {
      if (needImitate) {
        const filterScore = bang_action.wwScore.filter((_item2: any) => {
          const [__x, __y, __z] = _item2;
          return _z === __z;
        });
        // 获取当前赢法编号下的坐标情况
        filterScore &&
          ImitateLayout.push({ id: _z, coordenate: [_item, ...filterScore] });

        return;
      }
      // 按照curScore的赢法坐标二分法插入
      if (curScore.length > 0) {
        const findIndex = searchRange(curScore, _item);

        curScore.splice(findIndex, 0, _item);
      } else {
        curScore.push(_item);
      }
    }
  });
  if (needImitate) return { ImitateLayout, num };
  // 一种赢法有五颗棋子及表示胜利

  if (curScore.length >= 5) {
    for (let i = 4; i < curScore.length; i++) {
      if (curScore[i][2] == curScore[i - 4][2]) {
        bang_action.over = true;
        bang_action.tip = bang_action.turnMe ? "游戏胜利" : "游戏失败"
        isStart.value = false
        return;
      }
    }
  }

};
// target为赢法坐标
// 二分法插入，优先按照赢法坐标排序，其次按照x排序，再其次为y
const searchRange = (nums: any[], target: number[]) => {
  const [_x, _y, _z] = target;
  let start = 0,
    end = nums.length - 1;
  if (_z <= nums[start][2]) {
    return 0;
  }

  if (_z >= nums[end][2]) {
    return end + 1;
  }

  while (start <= end) {
    let mid = Math.floor((start + end) / 2); //10, 4, 7,5
    if (nums[mid][2] > _z) {
      end = mid - 1; //20,9,6,4
    } else {
      start = mid + 1; //0,5
    }
  }
  // 当赢法坐标相等时有才又要检测x,y
  if (_z == nums[start][2]) {
    // 由于优先向前插入，这块检测向右侧推进排序
    while (true) {
      if (_x <= nums[start][0]) {
        if (_y <= nums[start][1]) {
          break;
        }
      }
      ++start;
    }
  }
  return start;
};
const restart = () => {
  ++bang_action.key;
  Object.assign(bang_action, {
    turnMe: false,
    wwScore: [],
    opwScore: [],
    over: false,
    tip: "",
  });
  nextTick(() => initChessBoard());
};
onMounted(() => {
  socket.value = new IoSocket()

  socket.value?.on("matchSuccess", (data: any) => {
    ElMessage.success("匹配成功")
    isStart.value = true
    isMatching.value = false
    console.log(data);
    opponent.value = data.user;
    bang_action.turnMe = data.isStart
    bang_action.tip = data.isStart ? "该您落子" : "等待对方落子"
  })

  socket.value?.on("stepChese", (data: { x: number, y: number }) => {
    step(data.x, data.y)
  })
  socket.value?.on("tip", (msg: string) => {
    ElMessageBox.confirm(msg, { autofocus: false, closeOnClickModal: false, cancelButtonText: "重新匹配 ", confirmButtonText: "退出房间" },)
      .then(() => {
        console.log("退出");
      }).catch(() => {
        console.log("重新匹配");
        startMatch()
      }).finally(() => {
        isStart.value = false
        restart()
        opponent.value = {}
      })
    bang_action.tip = msg
  })
})
const startMatch = () => {
  let time = 0;
  isMatching.value = true
  bang_action.tip = `正在匹配中(${++time}s)...`
  timer = setInterval(() => {
    bang_action.tip = `正在匹配中(${++time}s)...`
  }, 1000)
  socket.value?.emit("match")

}

const cancelMatch = () => {
  isMatching.value = false
  clearInterval(timer);
  bang_action.tip = ""
}


</script>

<style scoped lang="scss">
@import "./index.scss";
</style>
